﻿// Shader-Based-Image-Processing (SBIP)
// http://code.google.com/p/sbip/
//
// Copyright © Frank Nagl, 2009-2011
// admin@franknagl.de
//
namespace SBIP.Filter.NonSBIP
{
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using Helper;

    /// <summary>
    /// This filter eliminates regions of a specified 
    /// <see cref="EliminateColor">color</see> by growing together the
    /// neighbour regions with other colors.
    /// </summary>
    /// 
    /// <remarks>
    /// <para>The filter accepts 24 bpp color images for processing.</para>  
    /// 
    /// <para>Sample usage:</para>
    /// <code>
    /// Bitmap bitmap = new Bitmap("Cathedral.jpg");
    /// NSColorEliminatedRegionGrowing filter = new NSColorEliminatedRegionGrowing();
    /// filter.EliminateColor = Color.White;
    /// bitmap = filter.Apply(bitmap);
    /// </code>
    /// 
    /// <para><b>Initial image:</b></para>
    /// <img src="../../Depthmap-with-white-edges.png"/>
    /// <para><b>Result image:</b></para>
    /// <img src="../../NSColorEliminatedRegionGrowing.png"/>
    /// </remarks>
    public class NSColorEliminatedRegionGrowing : BaseNonSBIPFilter
    {
        // list of neighbor colors of a pixel (to eliminate) and their occurrences
        private List<PixelRGBA> neighbors;

        /// <summary>Color, that will be eliminated by growing together the
        /// neighbour regions of pixels with this color.</summary>
        public Color EliminateColor { get; set; }

        /// <summary>
        /// Initializes a new instance of the 
        /// <see cref="NSColorEliminatedRegionGrowing"/> class.
        /// </summary>
        public NSColorEliminatedRegionGrowing()
        {
            SupportedSrcPixelFormats = PixelFormatFlags.Format24BppRgb;            
        }

        /// <summary>
        /// Extracts the color segments of the original bitmap as pixel-connected regions.
        /// </summary>
        protected override unsafe void Process(BitmapData srcData, BitmapData dstData)
        {
            const int ps = 3; // Image.GetPixelFormatSize(srcData.PixelFormat) / 8;
            int w = srcData.Width;
            int h = srcData.Height;
            int s = srcData.Stride;
            int offset = s - w * ps;

            byte* src = (byte*)srcData.Scan0.ToPointer();
            byte* dst = (byte*)dstData.Scan0.ToPointer();

            // copy src to dst            
            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++, src += ps, dst += ps)
                {
                    dst[RGBA.R] = src[RGBA.R];
                    dst[RGBA.G] = src[RGBA.G];
                    dst[RGBA.B] = src[RGBA.B];
                }
               // dst += offset; // do not do this offset addition
            }

            // start filter process
            dst = (byte*)dstData.Scan0.ToPointer();
            dst += s + ps;
            // for each line
            for (int y = 1; y < h - 1; y++)
            {
                // for each pixel
                for (int x = 1; x < w - 1; x++, dst+= ps)
                {
                    if (dst[RGBA.R] == EliminateColor.R &&
                        dst[RGBA.G] == EliminateColor.G &&
                        dst[RGBA.B] == EliminateColor.B)
                    {
                        neighbors = new List<PixelRGBA>(8);
                        // count neighbor colors
                        CountNeigbourColor(dst[RGBA.R - ps], dst[RGBA.G - ps], dst[RGBA.B - ps]);
                        CountNeigbourColor(dst[RGBA.R + ps], dst[RGBA.G + ps], dst[RGBA.B + ps]);
                        CountNeigbourColor(dst[RGBA.R - s], dst[RGBA.G - s], dst[RGBA.B - s]);
                        CountNeigbourColor(dst[RGBA.R + s], dst[RGBA.G + s], dst[RGBA.B + s]);
                        CountNeigbourColor(dst[RGBA.R - s - ps], dst[RGBA.G - s - ps], dst[RGBA.B - s - ps]);
                        CountNeigbourColor(dst[RGBA.R - s + ps], dst[RGBA.G - s + ps], dst[RGBA.B - s + ps]);
                        CountNeigbourColor(dst[RGBA.R + s + ps], dst[RGBA.G + s + ps], dst[RGBA.B + s + ps]);
                        CountNeigbourColor(dst[RGBA.R + s - ps], dst[RGBA.G + s - ps], dst[RGBA.B + s - ps]);

                        // find most occurrent color of neighbors
                        PixelRGBA tmpPx = new PixelRGBA();
                        foreach (var px in neighbors)
                        {
                            if (tmpPx.A < px.A)
                            {
                                tmpPx = px;
                            }
                        }

                        // use most occurrent color
                        dst[RGBA.R] = tmpPx.R;
                        dst[RGBA.G] = tmpPx.G;
                        dst[RGBA.B] = tmpPx.B;
                    }
                }
                dst += 2 * ps + offset;
            }
        }

        private void CountNeigbourColor(byte r, byte g, byte b)
        {
            if ((r == EliminateColor.R && g == EliminateColor.G) && b == EliminateColor.B)
            {
                return;
            }

            bool alreadyexists = false;
            foreach (PixelRGBA px in neighbors)
            {
                if (px.IsSameRGBColor(Color.FromArgb(r, g, b), Color.Black))
                {
                    px.A++;
                    alreadyexists = true;
                    break;
                }
            }

            if (!alreadyexists)
            {
                neighbors.Add(new PixelRGBA { A = 1, R = r, G = g, B = b });
            }
        }
    }
}
